﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Reflection;

namespace DotNETX.Utility.Config
{
    public class AppConfigUtils
    {
        /// <summary>
        /// Gets the value from the applications appSettings. This will error if the appSetting is not defined.
        /// </summary>
        /// <param name="appSettingName">Name of the appSettings key.</param>
        /// <returns></returns>
        public static T GetAppSetting<T>(string appSettingName) where T : IConvertible
        {
            return GetAppSettingInternal<T>(appSettingName, false, default(T));
        }

        /// <summary>
        /// Gets the value from the applications appSettings.
        /// </summary>
        /// <param name="appSettingName">Name of the appSettings key.</param>
        /// <param name="defaultValue">The default value returned if the appSetting has not been defined.</param>
        /// <returns></returns>
        public static T GetAppSetting<T>(string appSettingName, T defaultValue) where T : IConvertible
        {
            return GetAppSettingInternal<T>(appSettingName, true, defaultValue);
        }

        private static T GetAppSettingInternal<T>(string appSettingName, bool useDefaultOnUndefined, T defaultValue) where T : IConvertible
        {
            string value = ConfigurationManager.AppSettings[appSettingName];
            if (value != null)
            {
                return (T)Convert.ChangeType(value, typeof(T));
            }
            if (!useDefaultOnUndefined)
            {
                throw new Exception(string.Format("{0} not defined in appSettings.", appSettingName));
            }
            return defaultValue;
        }

        public static T GetAssemblySetting<T>(Assembly assembly, string appSettingName) where T : IConvertible
        {
            return GetAssemblySettingInternal<T>(assembly, appSettingName, false, default(T));
        }

        public static T GetAssemblySetting<T>(Type type, string appSettingName) where T : IConvertible
        {
            return GetAssemblySetting<T>(type.Assembly, appSettingName);
        }

        public static T GetAssemblySetting<T>(Assembly assembly, string appSettingName, T defaultValue) where T : IConvertible
        {
            return GetAssemblySettingInternal<T>(assembly, appSettingName, true, defaultValue);
        }

        public static T GetAssemblySetting<T>(Type type, string appSettingName, T defaultValue) where T : IConvertible
        {
            return GetAssemblySetting<T>(type.Assembly, appSettingName, defaultValue);
        }

        private static T GetAssemblySettingInternal<T>(Assembly assembly, string appSettingName, bool useDefaultOnUndefined, T defaultValue) where T : IConvertible
        {
            string value = null;
            System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(assembly.Location);
            foreach (string key in config.AppSettings.Settings.AllKeys)
            {
                if (key == appSettingName)
                {
                    value = config.AppSettings.Settings[appSettingName].Value;
                    break;
                }
            }
            if (value != null)
            {
                return (T)Convert.ChangeType(value, typeof(T));
            }
            if (!useDefaultOnUndefined)
            {
                throw new Exception(string.Format("{0} not defined in appSettings.", appSettingName));
            }
            return defaultValue;
        }

        public static string GetAppConnectionString(string connectionStringName)
        {
            if (connectionStringName == null)
            {
                throw new ArgumentNullException("connectionStringName");
            }
            ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[connectionStringName];
            if (settings == null)
            {
                throw new Exception(string.Format("No connection string settings with the name '{0}'.", connectionStringName));
            }
            return settings.ConnectionString;
        }

        //public static string GetAssemblyConnectionString(Assembly assembly, string connectionStringName)
        //{
        //    if (connectionStringName == null)
        //    {
        //        throw new ArgumentNullException("connectionStringName");
        //    }

        //    string value = null;
        //    System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(assembly.Location);
        //    ConnectionStringSettings settings = config.ConnectionStrings[connectionStringName];
        //    if (settings == null)
        //    {
        //        throw new Exception(string.Format("No connection string settings with the name '{0}'.", connectionStringName));
        //    }
        //    return settings.ConnectionString;
        //}

        public static bool HasAppSetting(string appSettingName)
        {
            foreach (string key in ConfigurationManager.AppSettings.AllKeys)
            {
                if (key == appSettingName)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool HasAssemblySetting(Assembly assembly, string appSettingName)
        {
            foreach (string key in ConfigurationManager.OpenExeConfiguration(assembly.Location).AppSettings.Settings.AllKeys)
            {
                if (key == appSettingName)
                {
                    return true;
                }
            }
            return false;
        }

        public static bool HasAssemblySetting(Type type, string appSettingName)
        {
            return HasAssemblySetting(type.Assembly, appSettingName);
        }

        public static bool HasAppConnectionString(string connectionStringName)
        {
            if (connectionStringName == null)
            {
                throw new ArgumentNullException("connectionStringName");
            }
            ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[connectionStringName];

            return settings != null;
        }

        //public static bool HasAssemblyConnectionString(Assembly assembly, string connectionStringName)
        //{
        //    if (connectionStringName == null)
        //    {
        //        throw new ArgumentNullException("connectionStringName");
        //    }

        //    string value = null;
        //    System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(assembly.Location);
        //    ConnectionStringSettings settings = config.ConnectionStrings[connectionStringName];
        //    return settings != null;
        //}

        //public static bool HasAssemblyConnectionString(Type type, string connectionStringName)
        //{
        //    return HasAssemblyConnectionString(type.Assembly, connectionStringName);
        //}

        public static T TryGetSetting<T>(Assembly assembly, string appSettingName) where T : IConvertible
        {
            if (HasAssemblySetting(assembly, appSettingName))
            {
                return GetAssemblySetting<T>(assembly, appSettingName);
            }
            return GetAppSetting<T>(appSettingName);
        }

        public static T TryGetSetting<T>(Type type, string appSettingName) where T : IConvertible
        {
            return TryGetSetting<T>(type.Assembly, appSettingName);
        }

        public static T TryGetSetting<T>(Assembly assembly, string appSettingName, T defaultValue) where T : IConvertible
        {
            if (HasAssemblySetting(assembly, appSettingName))
            {
                try
                {
                    return GetAssemblySetting<T>(assembly, appSettingName);
                }
                catch
                {
                }
            }
            return GetAppSetting<T>(appSettingName, defaultValue);
        }

        public static T TryGetSetting<T>(Type type, string appSettingName, T defaultValue) where T : IConvertible
        {
            return TryGetSetting<T>(type.Assembly, appSettingName, defaultValue);
        }

        //public static string TryGetConnectionString(Assembly assembly, string connectionStringName)
        //{
        //    if (HasAssemblyConnectionString(assembly, connectionStringName))
        //    {
        //        return GetAssemblyConnectionString(assembly, connectionStringName);
        //    }
        //    return GetAppConnectionString(connectionStringName);
        //}

        //public static string TryGetConnectionString(Type type, string connectionStringName)
        //{
        //    return TryGetConnectionString(type.Assembly, connectionStringName);
        //}

        public static void SetAppSetting(string appSettingName, string value)
        {
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings[appSettingName].Value = value;
            config.Save();
            ConfigurationManager.RefreshSection(appSettingName);
        }

        public static void SetAssemblySetting(Assembly assembly, string appSettingName, string value)
        {
            Configuration config = ConfigurationManager.OpenExeConfiguration(assembly.Location);
            config.AppSettings.Settings[appSettingName].Value = value;
            config.Save();
        }

        public static void SetAssemblySetting(Type type, string appSettingName, string value)
        {
            SetAssemblySetting(type.Assembly, appSettingName, value);
        }


    }
}
